package edu.kpi.pzks2.simulator.links;

import edu.kpi.pzks2.simulator.processing.Processor;

/**
 * Simplex channel. Connector between channel and processor.
 *
 * @author Pustovit Michael, pustovitm@gmail.com
 */
public class SimplexChannel {
    private final boolean isPackageModeOn;
    private final Channel channel;
    
    private final Processor procTo;
    private final Processor procFrom;

    private Message msg;
    private int leftTime;

    SimplexChannel(
            final Channel channel,
            final Processor procTo,
            final Processor procFrom,
            final boolean isPackageModeOn) {
        this.procTo = procTo;
        this.procFrom = procFrom;
        this.channel = channel;
        this.isPackageModeOn = isPackageModeOn;
    }

    /**
     * Tries to send message via this channel.
     * @param message the sending message
     * @param to destination processor
     * @return transmission success (is channel was already busy or destination processor isn't ready - false)
     */
    public boolean sendMessage(final Message message, final Processor to) {
        if (isTransmittingNow() || !readyToTransmitt()) return false;
        
        this.msg = message;
        leftTime = message.getWeight() * Channel.DUPLEX_BRAKING;

        // If package mode is on and linked processor isn't destination for this message - transmit this message
        // immediately.
        if (isPackageModeOn && !msg.getProcTo().equals(procTo)) {
            procTo.receiveMessage(message);
        }
        
        return true;
    }

    void makeStep(final int speed) {
        if (msg != null) {
            leftTime -= speed;
    
            if (leftTime <= 0) {
                // if package mode off or it is last transaction
                if (!isPackageModeOn || msg.getProcTo().equals(procTo)) {
                    procTo.receiveMessage(msg);
                }
                msg = null;
            }
        }
    }

    /**
     * @return is channel which contains this simplex channel is busy at the moment
     */
    public boolean isBusy() {
            return channel.isAnyBusyNow();
    }

    public boolean isTransmittingNow() {
        return leftTime > 0;
    }
    
    /**
     * @return destination processor of the channel
     */
    public Processor getLinkedProc() {
        return procTo;
    }

    private boolean readyToTransmitt() {
        // if channel is transmitting something now - it's busy
        if (isTransmittingNow()) { return false; }
        
        // if channel is duplex and pair (reverse) channel is busy already and this channel isn't - it's free
        if (channel.isDuplex() && channel.isAnyBusyNow()) { return true; } 
        
        // if channel is simlex and pair (reverse) channel is busy already and this channel 
        // isn't - whole channel is busy
        if (!channel.isDuplex() && channel.isAnyBusyNow()) { return false; }
        
        return procFrom.getFreeChannelCount() > 0 && procTo.getFreeChannelCount() > 0;
    }
    
    @Override
    public String toString() {
        return procFrom.getProcId() + " to " + procTo.getProcId() + " " + (isBusy() ? leftTime : "-");
    }

    public Message getMsg() {
        return msg;
    }
}
